#include "SQLWithSubqueryClause_Entry.h"

#include "../SQLObject.h"
#include "SQLSelectQueryBlock.h"
#include "../../../utils/ifeq.h"
#include "../../visitor/SQLASTVisitor.h"

SQLWithSubqueryClause_Entry::SQLWithSubqueryClause_Entry()
{
  columns = std::make_shared<std::list<SQLName_ptr>>();
}

SQLWithSubqueryClause_Entry::SQLWithSubqueryClause_Entry(string_ptr alias, SQLSelect_ptr select)
{
  columns = std::make_shared<std::list<SQLName_ptr>>();
  this->setAlias(alias);
  this->setSubQuery(select);
}

SQLWithSubqueryClause_Entry::SQLWithSubqueryClause_Entry(string_ptr alias, SQLExpr_ptr expr)
{
  columns = std::make_shared<std::list<SQLName_ptr>>();
  this->setAlias(alias);
  this->setExpr(expr);
}

void SQLWithSubqueryClause_Entry::cloneTo(SQLWithSubqueryClause_Entry_ptr x)
{
  for (SQLName_ptr column : *columns)
  {
    SQLName_ptr column2 = std::dynamic_pointer_cast<SQLName>(column->clone());
    // column2->setParent(x);
    x->columns->push_back(column2);
  }

  if (subQuery != NULL)
  {
    x->setSubQuery(std::dynamic_pointer_cast<SQLSelect>(subQuery->clone()));
  }

  if (returningStatement != NULL)
  {
    setReturningStatement(std::dynamic_pointer_cast<SQLStatement>(returningStatement->clone()));
  }

  x->alias = alias;
  x->expr = expr;
}

BOOL_ptr SQLWithSubqueryClause_Entry::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (flashback == expr)
  {
    setFlashback(target);
    return BOOL::TRUE;
  }

  for (int i = 0; i < columns->size(); i++)
  {
    // if (columns->get(i) == expr) {
    //     target->setParent(this);
    //     columns->set(i, (SQLName*) expr);
    //     return BOOL::TRUE;
    // }
  }
  return BOOL::FALSE;
}

SQLObject_ptr SQLWithSubqueryClause_Entry::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLWithSubqueryClause_Entry>(new SQLWithSubqueryClause_Entry());
  std::shared_ptr<SQLWithSubqueryClause_Entry> clone_res = std::dynamic_pointer_cast<SQLWithSubqueryClause_Entry>(clone_tmp);

  return clone_res;
}

SQLExpr_ptr SQLWithSubqueryClause_Entry::getExpr()
{
  return expr;
}

void SQLWithSubqueryClause_Entry::setExpr(SQLExpr_ptr expr)
{
  this->expr = expr;
}

void SQLWithSubqueryClause_Entry::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLWithSubqueryClause_Entry)))
  {
    auto it_columns = columns->begin();
    for (int i = 0; i < columns->size(); i++)
    {
      std::advance(it_columns, i);
      SQLExpr_ptr column = *it_columns;
      if (column != NULL)
      {
        column->accept(visitor);
      }
    }

    if (subQuery != NULL)
    {
      subQuery->accept(visitor);
    }

    if (returningStatement != NULL)
    {
      returningStatement->accept(visitor);
    }

    if (expr != NULL)
    {
      expr->accept(visitor);
    }
  }
  visitor->endVisit(SharedObject(SQLWithSubqueryClause_Entry));
}

SQLSelect_ptr SQLWithSubqueryClause_Entry::getSubQuery()
{
  return subQuery;
}

void SQLWithSubqueryClause_Entry::setSubQuery(SQLSelect_ptr subQuery)
{
  if (subQuery != NULL)
  {
    subQuery->setParent(SharedObject(SQLWithSubqueryClause_Entry));
  }
  this->subQuery = subQuery;
}

SQLStatement_ptr SQLWithSubqueryClause_Entry::getReturningStatement()
{
  return returningStatement;
}

void SQLWithSubqueryClause_Entry::setReturningStatement(SQLStatement_ptr returningStatement)
{
  if (returningStatement != NULL)
  {
    returningStatement->setParent(SharedObject(SQLWithSubqueryClause_Entry));
  }
  this->returningStatement = returningStatement;
}

SQLName_list_ptr SQLWithSubqueryClause_Entry::getColumns()
{
  return columns;
}

SQLTableSource_ptr SQLWithSubqueryClause_Entry::findTableSourceWithColumn(long columnNameHash, string_ptr columnName, int option)
{
  for (SQLName_ptr column : *columns)
  {
    if (column->nameHashCode64() == columnNameHash)
    {
      return SharedObject(SQLWithSubqueryClause_Entry);
    }
  }

  if (subQuery != NULL)
  {
    SQLSelectQueryBlock_ptr queryBlock = subQuery->getFirstQueryBlock();
    if (queryBlock != NULL)
    {
      if (queryBlock->findSelectItem(columnNameHash) != NULL)
      {
        return SharedObject(SQLWithSubqueryClause_Entry);
      }
    }
  }
  return NULL;
}

bool SQLWithSubqueryClause_Entry::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == NULL || getClass() != o->getClass())
  {
    return false;
  }
  if (!this->equals(o))
  {
    return false;
  }

  std::shared_ptr<SQLWithSubqueryClause_Entry> entry = std::dynamic_pointer_cast<SQLWithSubqueryClause_Entry>(o);

  if (!IFEQ::ifeq(columns, entry->columns))
  {
    return false;
  }
  if (subQuery != NULL ? !subQuery->equals(entry->subQuery) : entry->subQuery != NULL)
  {
    return false;
  }
  return returningStatement != NULL ? returningStatement == entry->returningStatement : entry->returningStatement == NULL;
}

int SQLWithSubqueryClause_Entry::hashCode()
{
  // int result = super->hashCode();
  int result = 0;
  // result = 31 * result + (columns != NULL ? columns->hashCode() : 0);
  result = 31 * result + (subQuery != NULL ? subQuery->hashCode() : 0);
  result = 31 * result + (returningStatement != NULL ? returningStatement->hashCode() : 0);
  return result;
}
